#ifndef SC_H
#define SC_H

///////////////////////////////////////////////////////////////////////////////
//BranchPoint
///////////////////////////////////////////////////////////////////////////////

class BranchPoint
{
public:
								BranchPoint( void );
								BranchPoint( int parent, int hopsFromBase );
	cg::Vec3					position;
	cg::Vec3					growDirection;
	int							growCount;	
	float						area;
	int							hopsFromBase;
	void						ResetGrow( void );
	int parent;	
	static float				MinArea;
};

CG_INLINE BranchPoint::BranchPoint( void )
{
	
}

CG_INLINE BranchPoint::BranchPoint( int parent, int hopsFromBase )
{	
	this->parent = parent;
	this->hopsFromBase = hopsFromBase;
	area = MinArea;
}

CG_INLINE void BranchPoint::ResetGrow( void )
{
	growDirection = cg::Vec3::ZERO;
	growCount = 0;
}

///////////////////////////////////////////////////////////////////////////////
//Tree
///////////////////////////////////////////////////////////////////////////////

class Tree
{
public:
	typedef std::vector< std::pair< int, int > >	Segments;
	Segments							segments;	
	//
										Tree( void );
	//									Simple render of tree graph with usinl glLineWidth
	void								Render( void );
	int									FindIndexOfClosestBranchPoint( const cg::Vec3& position ) const;
	BranchPoint &						GetBranchPoint( int index );	
	const BranchPoint &					GetBranchPoint( int index ) const;	
	//									add new nodes and edges based on previous run of SC algorithm
	bool								Update( float D );
	//									public to make posible adding trunk and etc.
	int									AddBranchPoint( int parentIndex, const cg::Vec3& position );
	void								AddSegment( int index1, int index2 );
	int									GetNumberOfBranchPoints( void ) const;
	//									smooths branching angles
	void								ReduceBranchingAngle( void );
	//									aproximate area of branchpoint
	void								ComputeLevel( void );

private:
	int									GetIndexOfLastBranchPoint() const;		
	void								IncrementBranch( int indexLeave );
	//
	typedef std::map< int, BranchPoint >			IndexedBranchPoints;	
	//
	IndexedBranchPoints					branchPoints;	
};

CG_INLINE Tree::Tree( void )
{

}

CG_INLINE BranchPoint &	Tree::GetBranchPoint( int index )
{
	return branchPoints.find( index )->second;
}

CG_INLINE const BranchPoint & Tree::GetBranchPoint( int index ) const
{
	return branchPoints.find( index )->second;
}

CG_INLINE int Tree::GetIndexOfLastBranchPoint() const
{
	return branchPoints.size() - 1;
}

CG_INLINE void Tree::AddSegment( int index1, int index2 )
{
	segments.push_back( std::make_pair( index1, index2 ) );	
}

CG_INLINE int Tree::GetNumberOfBranchPoints( void ) const
{
	return branchPoints.size();
}

///////////////////////////////////////////////////////////////////////////////
//AtractionPoint
///////////////////////////////////////////////////////////////////////////////

class AtractionPoint
{
public:
	int						closestBranchIndex;	
	cg::Vec3				position;
};

typedef	std::vector<AtractionPoint>		AtractionPoints;

///////////////////////////////////////////////////////////////////////////////
//Envelope
///////////////////////////////////////////////////////////////////////////////

class Envelope
{
public:					
										Envelope( float radius, const cg::Vec3& position, int number );
	void								Render();
	AtractionPoints::iterator			GetAtractionPointsBegin();
	AtractionPoints::iterator			GetAtractionPointsEnd();
	AtractionPoints::iterator			RemoveAtractionPoint( AtractionPoints::iterator	it );
	cg::Vec3							GetPosition() const;

private:
	cg::Vec3							position;
	std::vector<AtractionPoint>			atractionPoints;
	int									number;	
	float								radius;
};

CG_INLINE AtractionPoints::iterator Envelope::GetAtractionPointsBegin()
{
	return atractionPoints.begin();
}

CG_INLINE AtractionPoints::iterator Envelope::GetAtractionPointsEnd()
{
	return atractionPoints.end();
}

CG_INLINE AtractionPoints::iterator Envelope::RemoveAtractionPoint( AtractionPoints::iterator	it )
{
	return atractionPoints.erase( it );
}

cg::Vec3 CG_INLINE Envelope::GetPosition() const
{
	return position;
}

///////////////////////////////////////////////////////////////////////////////
//Compute grow for one iteration
///////////////////////////////////////////////////////////////////////////////

void ComputeGrewIteration( Envelope &envelope,
						   Tree &tree,
						   float D,
						   float infDistMultOfD,
						   float killDistMultOfD );

///////////////////////////////////////////////////////////////////////////////
// Generation of tree
///////////////////////////////////////////////////////////////////////////////

void BuildTree(	Envelope &envelope,
				Tree &tree,
				float D,
				float infDistMultOfD,
				float killDistMultOfD,			
				int levelOfSmoothing );

void BuildTreeDebug(	Envelope &envelope,
						Tree &tree,
						float D,
						float infDistMultOfD,
						float killDistMultOfD );

///////////////////////////////////////////////////////////////////////////////
// Generation of trunk
///////////////////////////////////////////////////////////////////////////////

void CreateTrunk( Tree &tree,
				  float D, 
				  float totalLen );

#endif